/*
* @Author: slp
* @Date:   2018-06-25 12:56:11
* @Last Modified by:   slp
* @Last Modified time: 2018-06-26 08:51:12
*/

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bfd.h"
#include "disassemble.h"
#include "assert.h"
#include "kifydisassembler.h"

#define BFD_BUFFER_LEN 32

struct KifyDisassemble
{
	void *kify_disas;
	char stream_buf[256];
	int stream_buf_inx;
	struct disassemble_info disas_info;
	bfd_byte bfd_buffer[BFD_BUFFER_LEN];
	// bfd_byte *bfd_buffer;
};


static int kify_print_func (void * _stream, const char* format, ...)
{
	struct KifyDisassemble *dis = (struct KifyDisassemble*)_stream;
	va_list ap;
	int ret = 0;
	va_start(ap, format);
	ret = vsprintf(dis->stream_buf+dis->stream_buf_inx, format, ap);
	dis->stream_buf_inx += ret;
	va_end(ap);
	return ret;
}
// Formatter for address in memory referencing instructions
static void print_address_aarch64(bfd_vma addr, struct disassemble_info *info){
	if ((long)addr >= 0)
		info->fprintf_func(info->stream, "#%-4ld ; {pc}+%ld", addr, addr);
	else
		info->fprintf_func(info->stream, "#%-4ld ; {pc}%ld", addr, addr);
}

const char *kify_disassemble(void * disas, unsigned long pc, unsigned int inst)
{
	struct KifyDisassemble* dis = (struct KifyDisassemble*)disas;
	assert(dis == dis->kify_disas);
	dis->stream_buf_inx = 0;

	dis->disas_info.buffer_vma = pc;
	memcpy(dis->bfd_buffer, &inst, sizeof(inst));

	print_insn_aarch64(pc, &dis->disas_info);
	return dis->stream_buf;
}

void *new_disassemble()
{
	struct KifyDisassemble *new_disas = (struct KifyDisassemble *)malloc(sizeof(struct KifyDisassemble));
	memset(new_disas, 0, sizeof(*new_disas));
	struct disassemble_info *disas_info_p = &new_disas->disas_info;

	// init default disas info val
	init_disassemble_info(disas_info_p, new_disas, kify_print_func);

	disas_info_p->print_address_func = print_address_aarch64;
	// disas_info_p->arch = bfd_arch_aarch64;
    // disas_info_p->mach = bfd_mach_aarch64;
    disas_info_p->buffer = new_disas->bfd_buffer;
    disas_info_p->buffer_length = BFD_BUFFER_LEN;
    disas_info_p->buffer_vma = 0;
    new_disas->kify_disas = new_disas;
    return new_disas;
}

void * release_disassemble(void *disas)
{
	struct KifyDisassemble* dis = (struct KifyDisassemble*)disas;
	assert(dis == dis->kify_disas);
	free(disas);
}
